package com.airlines.crewassignment.service.crew.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.airlines.common.ListSortUtil;
import com.airlines.crewassignment.constant.CrewTaskConstant;
import com.airlines.crewassignment.dao.CrewMapper;
import com.airlines.crewassignment.dao.FlightInfoMapper;
import com.airlines.crewassignment.domain.Crew;
import com.airlines.crewassignment.domain.CrewFlyDuty;
import com.airlines.crewassignment.domain.CrewTask;
import com.airlines.crewassignment.domain.DateTimeRange;
import com.airlines.crewassignment.domain.FlightInfo;
import com.airlines.crewassignment.entity.linkline.TaskLoopFlightFact;
import com.airlines.crewassignment.service.crew.CrewFylDutyService;
import com.airlines.crewassignment.service.crew.CrewTaskService;
import com.airlines.crewassignment.service.linkline.TaskLoopFlightFactService;

/**
 * @author xxxx
 * @date 2018年9月3日 
 * @description
 */
@Service
public class CrewFylDutyServiceImpl implements CrewFylDutyService{
	@Autowired
	private FlightInfoMapper flightInfoMapper;
	
	@Autowired
	private TaskLoopFlightFactService taskLoopFlightFactService;
	
	@Autowired
	private CrewMapper crewMapper;
	
	@Autowired
	private CrewTaskService crewTaskService;


	@Override
	public Map<String,List<CrewFlyDuty>> listCrewFlyDuty(DateTimeRange dateTimeRange, String dutyCode, String workcode) {
		List<Crew> crewList = crewMapper.listCrewBase(dutyCode, workcode);
		
		List<FlightInfo> flightInfoList = flightInfoMapper.listFlightInfoForCrew(dutyCode, dateTimeRange, workcode);
		ListSortUtil.sort(flightInfoList, true, "std");
		//任务环匹配
		taskLoopFlightFactService.flightInfoTaskLoopFlightFactListInit(dutyCode,flightInfoList);
		
		DateTime assignmentStart = new DateTime(dateTimeRange.getStart()).minusHours(24);
		DateTime assignmentEnd = new DateTime(dateTimeRange.getEnd()).plusHours(24);
		List<CrewTask> taskList =  crewTaskService.listCrewTaskForAssignment(dutyCode, workcode, new DateTimeRange(assignmentStart.toDate(),assignmentEnd.toDate()));

		
		Map<String,List<CrewFlyDuty>> map = new HashMap<>();
		for(Crew crew : crewList){
			List<FlightInfo> flightInfoListForCrew = new ArrayList<>();
			for(FlightInfo f : flightInfoList){
				if(f.getCrewWorkcodes().indexOf(crew.getWorkcode())>=0){
					flightInfoListForCrew.add(f);
				}
			}
			
			List<CrewTask> taskListForCrew = new ArrayList<>();
			for(CrewTask ct : taskList){
				if(StringUtils.equals(crew.getWorkcode(), ct.getWorkcode())){
					taskListForCrew.add(ct);
				}
			}
			
			List<CrewFlyDuty> cfdList = this.getCrewFlyDutyList(dutyCode, crew.getWorkcode(), flightInfoListForCrew, taskListForCrew);
			map.put(crew.getWorkcode(), cfdList);
						
		}
		
		return map;
	}
	
	/**
	 * 根据某个人的航班情况和公司安排的任务，计算执勤期列表
	 * @param flightInfoListForCrew 正序排序
	 * @param taskListForCrew 正序排序
	 * @return
	 */
	private  List<CrewFlyDuty> getCrewFlyDutyList(String dutyCode,String workcode,List<FlightInfo> flightInfoListForCrew,List<CrewTask> taskListForCrew ){
		Set<CrewFlyDuty> set = new HashSet<>();
		for(FlightInfo f : flightInfoListForCrew){
			CrewFlyDuty cfd = new CrewFlyDuty();
			cfd.setWorkcode(workcode);
			cfd.setFlightDate(f.getFlightDate());
			set.add(cfd);
		}
		
		for(CrewFlyDuty cfd : set){
			for(FlightInfo f : flightInfoListForCrew){
				if(cfd.getFlightDate().equals(f.getFlightDate())){
					cfd.getFlightInfoList().add(f);
				}
			}
		}
		
		//如果某一天的第一个航班（F1）起飞时刻-90-10小时内有任务，
		//则该任务T（开始时刻Ts--结束Te）算作该天执勤期开始时间，由于无法确定任务T结束后有没有休息期，故从Ts到航班开始时间段内全部算作飞行执勤期
		//除非公司能明确，任何任务T结束后，公司均安排适宜的休息场所，如果不在公司，又会牵扯到路程问题，这样就会很复杂
		//除了备份能明确，从备份报到时间开始，到航班准备前，可以不计入该日执勤期
		
		for(CrewFlyDuty cfd : set){
			if(CollectionUtils.isEmpty(cfd.getFlightInfoList())){
				cfd.setDutyTimeSum(0.0);
				continue;
			}
			
			Date flightInfoStd = cfd.getFlightInfoList().get(0).getStd();
			DateTime fristDateime = new DateTime(flightInfoStd).minusMinutes(90);
			//计算排班前的任务		
			List<CrewTask> oneDayDutyTaskList = this.listCrewTaskBeforeTime(taskListForCrew, 10, fristDateime);
					
			//计算该天的执勤期累积时间，单位小时
			Double dutyTimeSum = 0.0;
			Double restTimeSum = 0.0;
			
			//航班过站休息时间
			for(FlightInfo f : cfd.getFlightInfoList()){
				for(TaskLoopFlightFact tlff : f.getTaskLoopFlightFactList()){
					restTimeSum += tlff.getArrivalAirportRestTime()/60.00;
				}
			}
			
			//航前任务执勤时间
			for(CrewTask c : oneDayDutyTaskList){
				//备份任务在开始执行航班前不计入飞行执勤期
				if(!StringUtils.equals(c.getTaskType(),CrewTaskConstant.TASK_TYPE_BAK)){
					dutyTimeSum += c.getDateTimeRange().getDuration()/60.00;
				}	
			}
			
			//执勤结束时间
			FlightInfo last = cfd.getFlightInfoList().get(cfd.getFlightInfoList().size()-1);
			DateTime lastDateTime = new DateTime(last.getSta()).plusMinutes(20);
			if(last.getInnTimeAcars() != null){
				lastDateTime = new DateTime(last.getInnTimeAcars()).plusMinutes(20);
			}
			
			//总执勤期计算
			Duration duration = new Duration(fristDateime, lastDateTime);
			dutyTimeSum =  duration.getStandardMinutes()/60.0 - restTimeSum + dutyTimeSum;
			cfd.setDutyTimeSum(dutyTimeSum);
			
			//执勤期开始结束时间计算
			if(CollectionUtils.isNotEmpty(oneDayDutyTaskList)){
				Date start = oneDayDutyTaskList.get(oneDayDutyTaskList.size()-1).getStart();
				cfd.setStart(start);
			}else{
				cfd.setStart(fristDateime.toDate());
			}
			
			cfd.setEnd(lastDateTime.toDate());
			
		}
		
		return new ArrayList<>(set);
		
	}
	
	/**
	 * 在时间date往前推beforeHour小时范围内，获取最近CrewTask
	 * @param taskList
	 * @param beforeHour
	 * @param date
	 * @return
	 */
	private CrewTask getCrewTaskBeforeTime(List<CrewTask> taskList,int beforeHour,DateTime dateTime){
		if(CollectionUtils.isEmpty(taskList)){
			return null;
		}
		
		//最近的一个任务
		for(int i = taskList.size()-1 ; i>=0; i--){
			CrewTask c = taskList.get(i);
			if(c.getEnd().before(dateTime.toDate())){
				Duration duration = new Duration(new DateTime(c.getEnd()), dateTime);
				long mm = duration.getStandardMinutes();
				double diffHours =mm/60.00;
				if(diffHours<beforeHour && diffHours>=0){
					return c;
				}
			}else if(c.getEnd().after(dateTime.toDate()) && c.getStart().before(dateTime.toDate())){
				return c;
			}
		}

		return null;
	}
	
	/**
	 * 从dateTime往前依次查询，凡是dateTime与上一个时间小于beforeHour的，算作同一个执勤期的任务
	 * @param taskList
	 * @param beforeHour
	 * @param dateTime
	 * @return 该任务是倒叙排序的，距离dateTime最近的靠前
	 */
	private List<CrewTask> listCrewTaskBeforeTime(List<CrewTask> taskList,int beforeHour,DateTime dateTime){
		List<CrewTask> list = new ArrayList<>();
		boolean flag =true;
		
		while(flag){
			CrewTask c = this.getCrewTaskBeforeTime(taskList, beforeHour, dateTime);
			if(c != null ){
				list.add(c);
				dateTime = new DateTime(c.getStart());
			}else{
				flag = false;
			}
		}
		
		return list;
	}
	
}
